#include <htc.h>

// Total number of timer cycle to have 1 second (=4MHz / 256)
#define CYCLES_FOR_ONE_SEC 15625;

void increase();
void increaseMins();
void increaseHours();
void display();
void init();

// Hold the current hour and minute value
int h = 12;
int m = 0;

int main() {
    init();

    // Number of timer cycle left to have 1 second
    long int value = CYCLES_FOR_ONE_SEC;
    // Whether or not the current cycle has been counted
    short int counted = 0;
    // Number of seconds left before the next minute
    int sec = 60;

    // Current number of seconds the button has been pressed
    int timer = 0;
    // 1 means "currently setting the hours", 2 means "currently setting the minutes", 0 means none
    int setting = 0;

    // Used to store the current hour or minute values for blinking
    signed int currentTime = -1;
    
    // Used to store the state of the button on previous loop
    short int previousButtonState = 0;
    
    // Whether or not to use the next button release for the setting of time
    short int useNextButtonRelease = 1;

    while (1){
        // Make sure the loop never exceeds 128 cycles except when manually setting time
        
        // The first time in the first half of the timer cycle
        if (TMR0 - 128 < 0 && !counted) {
            counted = 1;
            
            // Every 15625 cycles, decrease the number of seconds
            if (--value == 0) {
                sec--;
                
                // Reset the timer cycle counter value
                value = CYCLES_FOR_ONE_SEC;
                
                // If the button is pressed, increase the number of seconds it has been pressed
                if (RB6) {
                    if (timer - 2 == 0) {
                        // Was setting the hours, and the hours are in the hidden phase of blinking: restore the hours
                        if (setting - 1 == 0 && currentTime + 1 != 0) {
                            PORTA = currentTime;
                            currentTime = -1;
                        }
                        // Was setting the minutes, and the minutes are in the hidden phase of blinking: restore the minutes
                        if (setting - 2 == 0 && currentTime + 1 != 0) {
                            PORTA = currentTime;
                            currentTime = -1;
                        }
                        
                        // If the button has been pressed for 2 seconds, get next setting value (0 -> 1 -> 2 -> 0)
                        if (++setting - 3 == 0) {
                            setting = 0;
                        }
                        timer = 0;
                        useNextButtonRelease = 0;
                    } else {
                        timer++;
                    }
                } else {
                    timer = 0;
                }

                // Currently setting the hours
                if (setting - 1 == 0) {
                    // Blink the hours
                    if (currentTime + 1 == 0) {
                        // If the currentTime is not set (ie -1), save time and turn hours off
                        currentTime = PORTA;
                        PORTA = 0;
                    } else {
                        // Otherwise turn hours on, and clear currentTime
                        PORTA = currentTime;
                        currentTime = -1;
                    }
                }
                
                // Currently setting the minutes
                if (setting - 2 == 0) {
                    // Blink the minutes
                    if (currentTime + 1 == 0) {
                        currentTime = PORTB;
                        PORTB = 0;
                    } else {
                        PORTB = currentTime;
                        currentTime = -1;
                    }
                }

                // Every 60 seconds, increase the minutes
                if (sec == 0) {
                    sec = 60;
                    increase();
                }
            }
        }
        
        // On the second half of the timer cycle, reset the counted so the next first half will be counted
        if (TMR0 - 128 > 0) {
           counted = 0;
        }

        // If the button is pressed
        if (RB6) {
            previousButtonState = 1;
        }
        
        // If the button is not pressed, but was on previous loop, ie it has been released and the setting did not change
        if (!RB6 && previousButtonState && useNextButtonRelease) {
            previousButtonState = 0;
            // Increase the currently setting time part
            if (setting = 1) {
                increaseHours();
            } else if (setting = 2) {
                increaseMins();
            }
        }
        
        // If the button is not pressed and it has been set that we should not use the next button release, then the next button release is valid
        if (!RB6 && !useNextButtonRelease) {
            useNextButtonRelease = 1;
        }
    }

    return 0;
}

// Increase the time by 1 minute
void increase() {
    // If the number of minutes is 60, then set to 0 and increase the hour
    if (++m - 60 == 0) {
        m = 0;
        
        // If the number of hours is 13, then set it to 1
        if (++h - 13 == 0) {
            h = 1;
        }
    }

    // Display the current time values
    display();
}

// Increase the number of minutes by 1, for setting purposes
void increaseMins() {
    if (++m - 60 == 0) {
        m = 0;
    }

    display();
}

// Increase the number of hours by 1, for setting purposes
void increaseHours() {
    if (++h - 13 == 0) {
        h = 1;
    }

    display();
}

// Displays the given values to hours and minutes
void display() {
    PORTA = h;
    PORTB = m;
}

// Initialisation
void init() {
    TRISA = 0; // RA are output

    TRISB = 0; // RB are output, except:
    TRISB7 = TRISB6 = 1; // ICSP and B6 -> switch

    // CONFIG register
	// Oscillator XT, no watchdog, no code protection
	__CONFIG(OSC_XT & WDT_OFF & CP_OFF);

    // OPTION_REG
    TMR0CS = 0; // timer0 in timer mode
    PSA = 1; // no prescaler timeout = 4M/256 = 15625Hz

    RA0 = RA1 = RA2 = RA3 =
    RB0 = RB1 = RB2 = RB3 = RB4 = RB5 = 0;
}